﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Xml;
//MicroDesigner
using Jc.MicroDesigner.Exceptions;
using Jc.MicroDesigner.Management;

namespace Jc.MicroDesigner.Objects
{
    /// <summary>
    /// Базовый класс для элементов с задержками
    /// Имеет множество входных пинов, и один выходной пин
    /// </summary>
    public abstract class RealElement : CircuitElement
    {
        /// <summary>
        /// Очередь сигналов для реализации задержек
        /// </summary>
        protected Queue<sbyte> _SignalQueue = new Queue<sbyte>();

        //Прошлые состояния пинов
        private int[] _LastTics = null;

        /// <summary>
        /// Задержка
        /// </summary>
        protected uint _Delay = 0;

        /// <summary>
        /// Выходной пин
        /// </summary>
        protected PIN _OutPIN = null;

        /// <summary>
        /// Сигнал по умолчанию
        /// </summary>
        protected sbyte _DefaultSignal = -1;

        /// <summary>
        /// Была ли выполнена полезная работа перед отправкой выходного сигнала
        /// </summary>
        protected bool _Performed = false;

        /// <summary>
        /// Инициализация выходного пина
        /// </summary>
        private void InitOut()
        {
            int j = 0;
            while ((j < _PINCollection.Count) && (_PINCollection[j].Type != PINType.Out))
                j++;
            if (j < _PINCollection.Count)
                _OutPIN = _PINCollection[j];
            else
                throw new NotValidPINsException(this);
            _LastTics = new int[_PINCollection.Count];
            for (int i = 0; i < _PINCollection.Count; i++)
                _LastTics[i] = -1;
        }

        /// <summary>
        /// Создать элемент с задержкой
        /// </summary>
        /// <param name="name">Имя</param>
        /// <param name="delay">Задержка</param>
        /// <param name="pins_count">Количество пинов</param>
        [Obsolete("Использовалось для тестов")]
        public RealElement(string name, uint delay, uint pins_count)
            : base(name, pins_count)
        {
            _Delay = delay;
            for (int i = 0; i < delay; i++)
                _SignalQueue.Enqueue(_DefaultSignal);
            InitOut();
        }

        /// <summary>
        /// Создать элемент используя файл конфигурации
        /// </summary>
        /// <param name="manager">Менеджер объектов</param>
        /// <param name="config">Файл конфигурации</param>
        public RealElement(ObjectManager manager, string config)
            : base(manager, config)
        {
            InitOut();
        }

        /// <summary>
        /// Функция элемента
        /// </summary>
        /// <returns>Значение на выходе</returns>
        protected abstract sbyte ComputeSignal();

        #region Override

        /// <summary>
        /// Выполнить работу
        /// </summary>
        /// <returns></returns>
        public override bool PerformAction()
        {
            bool action = false;
            if (_PINCollection.Count > 0)
            {
                int count = 0;
                int not_defined_count = 0;
                for (int i = 0; i < _PINCollection.Count; i++)
                {
                    if (_PINCollection[i].Type == PINType.In)
                    {
                        if ((_PINCollection[i].TicTime <= _LastTics[i]))
                            count++;
                        if (_PINCollection[i].Signal < 0)
                            not_defined_count++;
                    }
                }
                if ((count == 0) && (not_defined_count == 0))
                {
                    for (int pi = 0; pi < _PINCollection.Count; pi++)
                        _LastTics[pi] = _PINCollection[pi].TicTime;
                    sbyte CurrentSignal = ComputeSignal();
                    _SignalQueue.Enqueue(CurrentSignal);
                    _TicTime++;
                    _Performed = true;
                    action = true;
                }
            }
            return action;
        }

        /// <summary>
        /// Отправить данные
        /// </summary>
        /// <returns></returns>
        public override bool SendData()
        {
            //Отправка сигнала
            if (_Performed)
            {
                if (_SignalQueue.Count > 0)
                {
                    sbyte res = _SignalQueue.Dequeue();
                    if (res != -1)
                    {
                        _OutPIN.Send(res);
                        return true;
                    }
                }
                _Performed = false;
            }
            return false;
        }

        /// <summary>
        /// Обновить
        /// </summary>
        public override void Refresh()
        {
            base.Refresh();
            _SignalQueue.Clear();
            for (int i = 0; i < _Delay; i++)
                _SignalQueue.Enqueue(_DefaultSignal);
            for (int i = 0; i < _PINCollection.Count; i++)
                _LastTics[i] = -1;
        }

        /// <summary>
        /// Сохранить дополнительные свойства
        /// </summary>
        /// <param name="nodes">Ветви свойств</param>
        public override void SaveAdditionals(Dictionary<ConfigSection, XmlNode> nodes)
        {
            XmlNode additionals_node = nodes[ConfigSection.Additionals];
            XmlDocument doc = additionals_node.OwnerDocument;
            Hashtable props = new Hashtable();
            props.Add("Delay", Delay);
            props.Add("DefaultSignal", DefaultSignal);
            SchemeSerializer.SaveProps(additionals_node, props);
        }

        #endregion

        /// <summary>
        /// Задержка на элементе
        /// </summary>
        public uint Delay
        {
            get
            {
                return _Delay;
            }
            set
            {
                _SignalQueue.Clear();
                for (int i = 0; i < value; i++)
                    _SignalQueue.Enqueue(_DefaultSignal);
                _Delay = value;
            }
        }

        /// <summary>
        /// Сигнал по умолчанию
        /// </summary>
        public sbyte DefaultSignal
        {
            get
            {
                return _DefaultSignal;
            }
            set
            {
                _DefaultSignal = value;
            }
        }
    }
}